/*
 * COMP6471 Project
 *  
 * This file is created by Yuan Tao (ewan.msn@gmail.com)
 * Licensed under GNU GPL v3
 * 
 * $Author: ewan.msn@gmail.com $
 * $Date: 2011-10-12 04:12:44 +0000 (Wed, 12 Oct 2011) $
 * $Rev: 33 $
 * $HeadURL: http://comp6471.googlecode.com/svn/Project2/src/kklib/Layout.java $
 * 
 */

package kklib;

import java.util.ArrayList;


public class Layout {
	// attributes of the txt file.
	public String fileFullname;
	public ArrayList<String> txtFileData = new ArrayList<String>();
	
	// attributes of the layout
	public static final int MAX_GAME_SIZE = 6;
	public String gameName;
	public int gameSize = 4;
	public ArrayList<Cage> cages = new ArrayList<Cage>();
	private int numOfSolutions;			// number of possible solutions
	public static final int MAX_SOLUTIONS = 64;
	public static final int MAX_POSSIBLE_LAYOUTS = 30000;
	
	// to store the cells by connecting them in rows or columns
	public ArrayList<Cell> rows = new ArrayList<Cell>();
	public ArrayList<Cell> columns = new ArrayList<Cell>();
	
	
	// get property
	public int getNumOfSolutions() {
		return numOfSolutions;
	}
	
	// list of cages got from the input file of project 2.
	public ArrayList<Cage> availableCages = new ArrayList<Cage>();
	// result of project 2.
	public ArrayList<ArrayList<Integer>> possibleLayouts = new ArrayList<ArrayList<Integer>>();
	
	// get the next cage type from cage list.
	// the cages are stored in the list from the smallest type to the biggest one.
	private int getNextTypeOfCage(int curType) {
		for (int i = curType; i < availableCages.size(); i++) {
			if (availableCages.get(i).cageNum > 0) {
				return availableCages.get(i).cageType;
			}
		}
		
		return 0;
	}
	
	// get the type of a available cage from the list
	private int getTypeOfAvailableCage() {
		for (int i = 0; i < availableCages.size(); i++) {
			if (availableCages.get(i).cageNum > 0) {
				return availableCages.get(i).cageType;
			}
		}
		
		return 0;
	}
	
	// check if the cell of the layout has been filled in
	private boolean ifCellAdded(int x, int y) {
		Cage c;
		
		for (int i = 0; i < cages.size(); i++) {
			c = cages.get(i);
			for (int j = 0; j < c.cells.size(); j++) {
				if (c.cells.get(j).x == x && c.cells.get(j).y == y) {
					return true;
				}
			}
		}
		return false;
	}

	// check if the cage can be put into the layout
	private int ifCageFitIn(int curType) {
		int x = 0, y = 0;
		boolean exitLoop = false;
		
		// find the first empty cell by rows		
		for (y = 0; y < gameSize && !exitLoop; y++) {
			for (x = 0; x < gameSize; x++) {
				if (!ifCellAdded(x, y)) {
					exitLoop = true;
					y--;
					break;
				}
			}
		}
		
		// check if such type of cage can be fit into the layout
		boolean ifTypeOK = false;
		Cage c = new Cage();
		
		switch (curType) {
		case 1:		// 1 means type A
			ifTypeOK = true;		// always OK
			break;
		case 2:
			if (x + 1 < gameSize && !ifCellAdded(x + 1, y)) {
				Cell e = new Cell();
				
				e.x = x + 1;
				e.y = y;
				c.cells.add(e);
				ifTypeOK = true;
			}
			break;
		case 3:
			if (y + 1 < gameSize && !ifCellAdded(x, y + 1)) {
				Cell e = new Cell();
				
				e.x = x;
				e.y = y + 1;
				c.cells.add(e);
				ifTypeOK = true;
			}
			break;
		case 4:
			if (x + 2 < gameSize && !ifCellAdded(x + 1, y) && !ifCellAdded(x + 2, y)) {
				Cell e = new Cell();
				
				e.x = x + 1;
				e.y = y;
				c.cells.add(e);
				e = new Cell();
				e.x = x + 2;
				e.y = y;
				c.cells.add(e);
				ifTypeOK = true;
			}
			break;
		case 5:
			if (y + 2 < gameSize && !ifCellAdded(x, y + 1) && !ifCellAdded(x, y + 2)) {
				Cell e = new Cell();
				
				e.x = x;
				e.y = y + 1;
				c.cells.add(e);
				e = new Cell();
				e.x = x;
				e.y = y + 2;
				c.cells.add(e);
				ifTypeOK = true;
			}
			break;
		case 6:
			if (x + 1 < gameSize && y + 1 < gameSize 
					&& !ifCellAdded(x, y + 1) && !ifCellAdded(x + 1, y + 1)) {
				Cell e = new Cell();
				
				e.x = x;
				e.y = y + 1;
				c.cells.add(e);
				e = new Cell();
				e.x = x + 1;
				e.y = y + 1;
				c.cells.add(e);
				ifTypeOK = true;
			}
			break;
		case 7:
			if (x + 1 < gameSize && y + 1 < gameSize 
					&& !ifCellAdded(x, y + 1) && !ifCellAdded(x + 1, y)) {
				Cell e = new Cell();
				
				e.x = x;
				e.y = y + 1;
				c.cells.add(e);
				e = new Cell();
				e.x = x + 1;
				e.y = y;
				c.cells.add(e);
				ifTypeOK = true;
			}
			break;
		case 8:
			if (x + 1 < gameSize && y + 1 < gameSize 
					&& !ifCellAdded(x + 1, y) && !ifCellAdded(x + 1, y + 1)) {
				Cell e = new Cell();
				
				e.x = x + 1;
				e.y = y;
				c.cells.add(e);
				e = new Cell();
				e.x = x + 1;
				e.y = y + 1;
				c.cells.add(e);
				ifTypeOK = true;
			}
			break;
		case 9:
			if (x > 0 && y + 1 < gameSize 
					&& !ifCellAdded(x, y + 1) && !ifCellAdded(x - 1, y + 1)) {
				Cell e = new Cell();
				
				e.x = x;
				e.y = y + 1;
				c.cells.add(e);
				e = new Cell();
				e.x = x - 1;
				e.y = y + 1;
				c.cells.add(e);
				ifTypeOK = true;
			}
			break;
			
		case 10:
			if (x + 4 < gameSize 
					&& !ifCellAdded(x + 1, y) && !ifCellAdded(x + 2, y)
					&& !ifCellAdded(x + 3, y) && !ifCellAdded(x + 4, y)) {
				Cell e = new Cell();
				
				e.x = x + 1;
				e.y = y;
				c.cells.add(e);
				e = new Cell();
				e.x = x + 2;
				e.y = y;
				c.cells.add(e);
				e = new Cell();
				e.x = x + 3;
				e.y = y;
				c.cells.add(e);
				e = new Cell();
				e.x = x + 4;
				e.y = y;
				c.cells.add(e);
				ifTypeOK = true;
			}
			break;
			
		case 11:
			if (x + 2 < gameSize && y + 4 < gameSize
					&& !ifCellAdded(x, y + 1) && !ifCellAdded(x, y + 2)
					&& !ifCellAdded(x, y + 3) && !ifCellAdded(x, y + 4)
					&& !ifCellAdded(x + 1, y + 4) && !ifCellAdded(x + 2, y + 4)) {
				Cell e = new Cell();
				
				e.x = x;
				e.y = y + 1;
				c.cells.add(e);
				e = new Cell();
				e.x = x;
				e.y = y + 2;
				c.cells.add(e);
				e = new Cell();
				e.x = x;
				e.y = y + 3;
				c.cells.add(e);
				e = new Cell();
				e.x = x;
				e.y = y + 4;
				c.cells.add(e);
				e = new Cell();
				e.x = x + 1;
				e.y = y + 4;
				c.cells.add(e);
				e = new Cell();
				e.x = x + 2;
				e.y = y + 4;
				c.cells.add(e);
				ifTypeOK = true;
			}
			break;
			
		case 12:
			if (x + 2 < gameSize && y + 4 < gameSize
					&& !ifCellAdded(x, y + 1) && !ifCellAdded(x, y + 2)
					&& !ifCellAdded(x, y + 3)
					&& !ifCellAdded(x + 1, y) && !ifCellAdded(x + 2, y)
					&& !ifCellAdded(x + 3, y) && !ifCellAdded(x + 4, y)
					&& !ifCellAdded(x + 1, y + 3) && !ifCellAdded(x + 2, y + 3)) {
				Cell e = new Cell();
				
				e.x = x;
				e.y = y + 1;
				c.cells.add(e);
				e = new Cell();
				e.x = x;
				e.y = y + 2;
				c.cells.add(e);
				e = new Cell();
				e.x = x;
				e.y = y + 3;
				c.cells.add(e);
				e = new Cell();
				e.x = x + 1;
				e.y = y;
				c.cells.add(e);
				e = new Cell();
				e.x = x + 2;
				e.y = y;
				c.cells.add(e);
				e = new Cell();
				e.x = x + 3;
				e.y = y;
				c.cells.add(e);
				e = new Cell();
				e.x = x + 4;
				e.y = y;
				c.cells.add(e);
				e = new Cell();
				e.x = x + 1;
				e.y = y + 3;
				c.cells.add(e);
				e = new Cell();
				e.x = x + 2;
				e.y = y + 3;
				c.cells.add(e);
				ifTypeOK = true;
			}
			break;
		}
		
		if (!ifTypeOK) {
			return -1;
		}

		Cell e = new Cell();
		e.x = x;
		e.y = y;
		c.cells.add(e);		// add current cell as well

		// add the cage to the layout
		c.cageType = curType;
		cages.add(c);

		// decrease one available cage from cage list
		availableCages.get(curType - 1).cageNum--;
		
		// check if layout is full of cells.
		int cnt = 0;
		
		for (int i = 0; i < cages.size(); i++) {
			cnt += cages.get(i).cells.size();
		}
		if (cnt < gameSize * gameSize) {
			return 1;		// layout is not full
		}

		return 0;			// layout is full
	}
	
	// delete the last cage added to the layout
	private int deleteLastCage() {
		int index = cages.size() - 1;
		
		if (index < 0) {
			return 0;
		}
		
		// increase the number of available cage
		int ret = cages.get(index).cageType;
		availableCages.get(ret - 1).cageNum++;	
				
		// remove the last cage		
		cages.remove(index);
		return ret;
	}
	
	// replace the last cage with next type of available cages from the list
	private int replaceLastCage() {		
		int curType = 0;
		
		do {
			curType = deleteLastCage();
			if (curType == 0) {
				// have tried all the cages including the first one
				return 0;
			}
			curType = getNextTypeOfCage(curType);
		} while (curType == 0);
		
		return curType;
	}
	
	// store the result to be printed
	private int copyResult(ArrayList<Integer> pLayout) {
		for (int i = 0; i < gameSize * gameSize; i++) {
			pLayout.add(0);
		}
		
		ArrayList<Cell> c;
		int pad = 1;
		
		for (int i = 0; i < cages.size(); i++) {
			c = cages.get(i).cells;
			for (int j = 0; j < c.size(); j++) {
				pLayout.set(c.get(j).x + c.get(j).y * gameSize, pad);
			}
			pad++;
		}
		return 0;
	}
	
	// generate all the possible layouts from the list of cages. 
	public int generatePossibleLayoutFromCages() {
		int curType = 0, retIfCageFit = 0;
		//ArrayList<Cage> possibleLayout = new ArrayList<Cage>();

		curType = getTypeOfAvailableCage();
		while (curType > 0) {
			retIfCageFit = ifCageFitIn(curType);
			
			// the whole layout is covered by cages.
			if (retIfCageFit == 0) {
				if (getTypeOfAvailableCage() == 0) {
					// no available cage left. find one possible layout. 
					if (numOfSolutions < MAX_SOLUTIONS) {
						ArrayList<Integer> possibleLayout = new ArrayList<Integer>();
						if (copyResult(possibleLayout) == 0) {
							possibleLayouts.add(possibleLayout);
						}
					}
					numOfSolutions++;
					if (numOfSolutions > MAX_POSSIBLE_LAYOUTS) {
						return -3;		// too many results
					}
					
					// try to find other possible layouts
					curType = replaceLastCage();
				} else {
					// too many cages
					return -1;
				}
				
			// part of layout is not covered by cages
			} else if (retIfCageFit == 1) {
				curType = getTypeOfAvailableCage();
				if (curType == 0) {
					return -2; 	// not enough cages.
				}
				
			// the cage cannot be fit into the layout
			} else {
				// try next type of cage
				curType = getNextTypeOfCage(curType);
				if (curType == 0) {
					curType = replaceLastCage();
				}
			}
		}
		return numOfSolutions;
	}
}
